﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;
using System.Web.UI;
using System.Web.WebPages;

namespace jstreeMVCWrapper.Helpers
{
	public class JSTreeViewModel<T>
	{
		public IEnumerable<T> Items { get; set; }
		public Func<T, IEnumerable<T>> ChildrenProperty { get; set; }
		public Func<T, HelperResult> ItemTemplate { get; set; }
		public Func<T, string> ItemType { get; set; }
		public Func<T, bool> ItemIsSelected { get; set; }
		public Func<T, string> ItemID { get; set; }

		public IEnumerable<string> Plugins { get; set; }
		public IDictionary<string,object> CoreConfig { get; set; }
		public IDictionary<string,object> TypesConfig { get; set; }

		public string OnNodeSelect { get; set; }
	}
	
	public static class JSTreeViewHelper
	{
		/// <summary>
		/// Create an HTML tree from a recursive collection of items
		/// </summary>
		public static JSTreeView<T> JSTreeView<T>(this HtmlHelper html, JSTreeViewModel<T> model)
		{
			return new JSTreeView<T>(html, model);
		}

		public static JSTreeView<T> JSTreeView<T>(this HtmlHelper html, IEnumerable<T> items)
		{
			return new JSTreeView<T>(html, items);
		}
	}

	/// <summary>
	/// Create an HTML tree from a resursive collection of items
	/// </summary>
	public class JSTreeView<T> : IHtmlString
	{
		private readonly HtmlHelper _html;
		private JSTreeViewModel<T> _model;
		private string _treeID;

		public JSTreeView(HtmlHelper html, JSTreeViewModel<T> model)
		{
			if (html == null) throw new ArgumentNullException("html");
			_html = html;
			_model = model;
			_treeID = Guid.NewGuid().ToString("N");
		}
		public JSTreeView(HtmlHelper html, IEnumerable<T> items)
		{
			if (html == null) throw new ArgumentNullException("html");
			_html = html;
			_model = new JSTreeViewModel<T>();
			_model.Items = items;
			_treeID = Guid.NewGuid().ToString("N");
		}

		public JSTreeView<T> ContrainerID(string i_ID)
		{
			_treeID = i_ID;
			return this;
		}

		public JSTreeView<T> OnNodeSelect(string i_FunctionName)
		{
			_model.OnNodeSelect = i_FunctionName;
			return this;
		}

		public JSTreeView<T> Plugins(params string[] i_Plugins)
		{
			if (i_Plugins == null) throw new ArgumentNullException("Plugins");
			_model.Plugins = i_Plugins;
			return this;
		}

		public JSTreeView<T> CoreConfig(IDictionary<string,object> i_CoreConfig)
		{
			if (i_CoreConfig == null) throw new ArgumentNullException("CoreConfig");
			_model.CoreConfig = i_CoreConfig;
			return this;
		}

		public JSTreeView<T> CoreConfig(object i_CoreConfig)
		{
			if (i_CoreConfig == null) throw new ArgumentNullException("CoreConfig");
			_model.CoreConfig = anonymousObjectToDictionary(i_CoreConfig);
			return this;
		}

		public JSTreeView<T> TypesConfig(IDictionary<string, object> i_TypesConfig)
		{
			if (i_TypesConfig == null) throw new ArgumentNullException("TypesConfig");
			_model.TypesConfig = i_TypesConfig;
			return this;
		}

		public JSTreeView<T> TypesConfig(object i_TypesConfig)
		{
			if (i_TypesConfig == null) throw new ArgumentNullException("TypesConfig");
			_model.TypesConfig = anonymousObjectToDictionary(i_TypesConfig);
			return this;
		}

		private IDictionary<string, object> anonymousObjectToDictionary(object i_Object)
		{
			var objectProperties = i_Object.GetType().GetProperties();

			Dictionary<string, object> objectDictionary = objectProperties.ToDictionary(x => x.Name, x => x.GetValue(i_Object));
			return objectDictionary;
		}

		/// <summary>
		/// The template used to render each li item in the tree view
		/// </summary>
		public JSTreeView<T> ItemTemplate(Func<T, HelperResult> itemTemplate)
		{
			if (itemTemplate == null) throw new ArgumentNullException("itemTemplate");
			_model.ItemTemplate = itemTemplate;
			return this;
		}

		/// <summary>
		/// The property which returns the children items
		/// </summary>
		public JSTreeView<T> Children(Func<T, IEnumerable<T>> selector)
		{
			if (selector == null) throw new ArgumentNullException("selector");
			_model.ChildrenProperty = selector;
			return this;
		}

		public JSTreeView<T> ItemType(Func<T, string> selector)
		{
			if (selector == null) throw new ArgumentNullException("selector");
			_model.ItemType = selector;
			return this;
		}

		public JSTreeView<T> IsSelected(Func<T, bool> selector)
		{
			if (selector == null) throw new ArgumentNullException("selector");
			_model.ItemIsSelected = selector;
			return this;
		}

		public JSTreeView<T> ItemID(Func<T, string> selector)
		{
			if (selector == null) throw new ArgumentNullException("selector");
			_model.ItemID = selector;
			return this;
		}


		public string ToHtmlString()
		{
			TagBuilder divContainer = new TagBuilder("div");
			divContainer.Attributes.Add("id", _treeID);
			divContainer.Attributes.Add("data-js-tree-enabled", "true");
			divContainer.Attributes.Add("data-js-tree-on-node-select", _model.OnNodeSelect);
			divContainer.Attributes.Add("data-js-tree-plugins", Json.Encode(_model.Plugins));
			divContainer.Attributes.Add("data-js-tree-core", Json.Encode(_model.CoreConfig));
			divContainer.Attributes.Add("data-js-tree-types", Json.Encode(_model.TypesConfig));
			divContainer.InnerHtml = getHtmlListForItems(_model.Items);

			return divContainer.ToString();
		}


		private string getHtmlListForItems(IEnumerable<T> i_Items)
		{
			TagBuilder ul = new TagBuilder("ul");
			foreach (var item in i_Items)
			{
				TagBuilder li = new TagBuilder("li");
				li.InnerHtml = getItemTemplate(item);
				li.Attributes.Add("id", getItemID(item));
				li.Attributes.Add("data-jstree", getItemJSTreeData(item));
				
				if (itemHasChilder(item))
				{
					ul.InnerHtml += li.ToString(TagRenderMode.StartTag);
					ul.InnerHtml += li.InnerHtml;
					ul.InnerHtml += getHtmlListForItems(getItemChildren(item));
					ul.InnerHtml += li.ToString(TagRenderMode.EndTag);
				}
				else
				{
					ul.InnerHtml += li.ToString();
				}

			}

			return ul.ToString();
		}



		private string getItemJSTreeData(T item)
		{
			var retVal = "";
			var jsonJSTreeItemData = new { type = getItemType(item), selected = getItemIsSelected(item), opened = getItemIsSelected(item) };
			retVal = Json.Encode(jsonJSTreeItemData);
			return retVal;
		}

		private IEnumerable<T> getItemChildren(T i_ParentItem)
		{
			return (_model.ChildrenProperty(i_ParentItem) != null) ? _model.ChildrenProperty(i_ParentItem).ToList() : null;
		}

		private string getItemType(T i_Item)
		{
			return _model.ItemType(i_Item);
		}

		private string getItemID(T i_Item)
		{
			return _model.ItemID(i_Item);
		}

		private bool getItemIsSelected(T i_Item)
		{
			return _model.ItemIsSelected(i_Item);
		}

		private string getItemTemplate(T i_Item)
		{
			return _model.ItemTemplate(i_Item).ToHtmlString();
		}

		private bool itemHasChilder(T i_Item)
		{
			return ((getItemChildren(i_Item) != null) && (getItemChildren(i_Item).Count() > 0));
		}

	}
}